<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Snake in Mono Winforms</title>
<link rel="stylesheet" href="/cfg/format.css" type="text/css">
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="keywords" content="Mono, C#, Winforms, games, tutorial, Snake, programming, GUI, multiplatform">
<meta name="description" content="Snake in Mono Winforms">
<meta name="language" content="en">
<meta name="author" content="Jan Bodnar">
<meta name="distribution" content="global">

<script type="text/javascript" src="/lib/jquery.js"></script>
<script type="text/javascript" src="/lib/common.js"></script>

</head>

<body>

<div class="container">

<div id="wide_ad" class="ltow">
<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* 160x600, August 2011 */
google_ad_slot = "2484182563";
google_ad_width = 160;
google_ad_height = 600;
//-->
</script>
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>

<div class="content">


<a href="/" title="Home">Home</a>&nbsp;
<a href="..">Contents</a>


<h1>Snake in Mono Winforms</h1>

<p>
In this part of the Mono Winforms programming tutorial, we will create a Snake game clone. 
</p>

<script type="text/javascript"><!--
google_ad_client = "pub-9706709751191532";
/* NewSquare */
google_ad_slot = "0364418177";
google_ad_width = 300;
google_ad_height = 250;
//-->
</script> 
<script type="text/javascript"
src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> 
</script> 

<h2>Snake game</h2>

<p>
<b>Snake</b> is an older classic video game. It was first created in late 70s. 
Later it was brought to PCs. In this game the player
controls a snake. The objective is to eat as many apples as possible. 
Each time the snake eats an apple, its body grows. The snake must avoid 
the walls and its own body. This game is sometimes called <b>Nibbles</b>. 
</p>


<h2>Development</h2>

<p>
The size of each of the joints of a snake is 10px. The snake is controlled with 
the cursor keys. Initially the snake has three joints. The game is started by 
pressing one of the cursor keys. If the game is finished, we display "Game Over" 
message in the middle of the Board. 
</p>

<div class="codehead">Board.cs</div>
<pre class="code">
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;



public class Board : UserControl {

    private const int WIDTH = 300;
    private const int HEIGHT = 300;
    private const int DOT_SIZE = 10;
    private const int ALL_DOTS = 900;
    private const int RAND_POS = 27;

    private int[] x = new int[ALL_DOTS];
    private int[] y = new int[ALL_DOTS];

    private int dots;
    private int apple_x;
    private int apple_y;

    private bool left = false;
    private bool right = true;
    private bool up = false;
    private bool down = false;
    private bool inGame = true;

    private Timer timer;

    private Bitmap dot;
    private Bitmap apple;
    private Bitmap head;

    private IContainer components;
    
    public int BORDER_WIDTH;
    public int TITLEBAR_HEIGHT;

    public Board() {

        components = new Container();
        BackColor = Color.Black;
        DoubleBuffered = true;
        this.ClientSize = new Size(WIDTH, HEIGHT);

        try {
            dot = new Bitmap("dot.png");
            apple = new Bitmap("apple.png");
            head = new Bitmap("head.png");

        } catch (Exception e) {
            Console.WriteLine(e.Message);
            Environment.Exit(1);
        } 

        initGame();
    }

    private void OnTick(object sender, EventArgs e) {

        if (inGame) {
            checkApple();
            checkCollision();
            move();
        }
        this.Refresh();
    }


    private void initGame() {

        dots = 3;

        for (int z = 0; z < dots; z++) {
            x[z] = 50 - z * 10;
            y[z] = 50;
        }

        locateApple();
        KeyUp += new KeyEventHandler(OnKeyUp);


        timer = new Timer(this.components);
        timer.Enabled = true;
        timer.Interval = 100;
        timer.Tick += new System.EventHandler(this.OnTick);

        Paint += new PaintEventHandler(this.OnPaint);


    }


    private void OnPaint(object sender, PaintEventArgs e) {

        Graphics g = e.Graphics;

        if (inGame) {

            g.DrawImage(apple, apple_x, apple_y);

            for (int z = 0; z < dots; z++) {
                if (z == 0) {
                    g.DrawImage(head, x[z], y[z]);
                } else {
                    g.DrawImage(dot, x[z], y[z]);    
                }
            }

        } else {
            gameOver(g);
        }
    }


    private void gameOver(Graphics g) {

        String msg = "Game Over";
        StringFormat format = new StringFormat();
        format.Alignment = StringAlignment.Center;
        format.LineAlignment = StringAlignment.Center;
        
        g.DrawString(msg, Font, Brushes.White, ClientRectangle, format);
        timer.Stop();
    }


    private void checkApple() {

        if ((x[0] == apple_x) &amp;&amp; (y[0] == apple_y)) {
            dots++;
            locateApple();
        }
    }

    private void move() {

        for (int z = dots; z > 0; z--) {
            x[z] = x[(z - 1)];
            y[z] = y[(z - 1)];
        }

        if (left) {
            x[0] -= DOT_SIZE;
        }

        if (right) {
            x[0] += DOT_SIZE;
        }

        if (up) {
            y[0] -= DOT_SIZE;
        }

        if (down) {
            y[0] += DOT_SIZE;
        }
    }

    private void checkCollision() {

        for (int z = dots; z > 0; z--) {
            if ((z > 4) &amp;&amp; (x[0] == x[z]) &amp;&amp; (y[0] == y[z])) {
                inGame = false;
            }
        }

        if (y[0] > HEIGHT - DOT_SIZE - TITLEBAR_HEIGHT - BORDER_WIDTH) {
            inGame = false;
        }

        if (y[0] < 0) {
            inGame = false;
        }

        if (x[0] > WIDTH - DOT_SIZE - 2 * BORDER_WIDTH) {
            inGame = false;
        }

        if (x[0] < 0) {
            inGame = false;
        }
    }

    private void locateApple() {
        Random rand = new Random();
        int r = (int)(rand.Next(RAND_POS));
        apple_x = ((r * DOT_SIZE));
        r = (int)(rand.Next(RAND_POS));
        apple_y = ((r * DOT_SIZE));
    }


    private void OnKeyUp(object sender, KeyEventArgs e) {

        int key = (int) e.KeyCode;

        if ((key == (int) Keys.Left) &amp;&amp; (!right)) {
            left = true;
            up = false;
            down = false;
        }

        if ((key == (int) Keys.Right) &amp;&amp; (!left)) {
            right = true;
            up = false;
            down = false;
        }

        if ((key == (int) Keys.Up) &amp;&amp; (!down)) {
            up = true;
            right = false;
            left = false;
        }

        if ((key == (int) Keys.Down) &amp;&amp; (!up)) {
            down = true;
            right = false;
            left = false;
        }
    }
}
</pre>


<p>
First we will define the constants used in our game. 
</p>


<p>
The <b class="keyword">WIDTH</b> and <b class="keyword">HEIGHT</b> constants 
determine the size of the Board. The <b class="keyword">DOT_SIZE</b> is the 
size of the apple and the dot of the snake. The <b class="keyword">ALL_DOTS</b> 
constant defines the maximum number of possible dots on the Board. (900 = 300*300/10*10)
The <b class="keyword">RAND_POS</b> constant is used to calculate a random position of 
an apple. The <b class="keyword">DELAY</b> constant determines the speed of the game.
</p>

<pre class="explanation">
 private int[] x = new int[ALL_DOTS];
 private int[] y = new int[ALL_DOTS];
</pre>

<p>
These two arrays store x, y coordinates of all joints of a snake. 
</p>


<p>
In the <b class="keyword">move()</b> method we have the key algorithm 
of the game. To understand it, look at how the snake
is moving. You control the head of the snake. You can change its direction 
with the cursor keys. The rest of the joints move
one position up the chain. The second joint moves where the first was, 
the third joint where the second was etc. 
</p>

<pre class="explanation">
 for (int z = dots; z > 0; z--) {
     x[z] = x[(z - 1)];
     y[z] = y[(z - 1)];
 }
</pre>

<p>
This code moves the joints up the chain.
</p>

<pre class="explanation">
 if (left) {
     x[0] -= DOT_SIZE;
 }
</pre>

<p>
Move the head to the left.
</p>

<p>
In the <b class="keyword">checkCollision()</b> method, we determine 
if the snake has hit itself or one of the walls. 
</p>

<pre class="explanation">
 for (int z = dots; z > 0; z--) {

     if ((z > 4) &amp;&amp; (x[0] == x[z]) &amp;&amp; (y[0] == y[z])) {
         inGame = false;
     }
 }
</pre>

<p>
Finish the game, if the snake hits one of its joints with the head. 
</p>

<pre class="explanation">
 if (y[0] > HEIGHT - DOT_SIZE - TITLEBAR_HEIGHT - BORDER_WIDTH) {
     inGame = false;
 }
</pre>

<p>
Finish the game, if the snake hits the bottom of the Board. 
</p>

<div class="codehead">Snake.cs</div>
<pre class="code">
using System;
using System.Drawing;
using System.Windows.Forms;

class Snake : Form {

    public Snake() {

        Text = "Snake";
        DoubleBuffered = true;
        FormBorderStyle = FormBorderStyle.FixedSingle;
        
        int borderWidth = (this.Width - this.ClientSize.Width) / 2;
        int titleBarHeight = this.Height - this.ClientSize.Height - borderWidth;

        Board board = new Board();
        board.BORDER_WIDTH = borderWidth;
        board.TITLEBAR_HEIGHT = titleBarHeight;

        Controls.Add(board);
        CenterToScreen();

    }
}

class MApplication {
    public static void Main() {
        Application.Run(new Snake());
    }
}
</pre>

<p>
This is the main class. 
</p>


<br>
<img src="/img/gui/sharpwinf/snake.png" alt="Snake">
<div class="figure">Figure: Snake</div>

<hr class="btm">

<p>
This was the Snake game programmed using the Mono Winforms library.
</p>


<div class="botNav, center">
<span class="botNavItem"><a href="/">Home</a></span> ‡ <span class="botNavItem"><a href="..">Contents</a></span> ‡
<span class="botNavItem"><a href="#">Top of Page</a></span>
</div>

<div class="footer">
<div class="signature">
<a href="/">ZetCode</a> last modified December 17, 2008  <span class="copyright">&copy; 2007 - 2012 Jan Bodnar</span>
</div>
</div>

</div> <!-- content -->

</div> <!-- container -->

</body>
</html>
